/*
 * Copyright (C) 2011 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.smartandroid.sa.json;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Map;
import java.util.Set;

import com.smartandroid.sa.json.internal.$Gson$Types;

/**
 * Default serialization and deserialization of a map type. This implementation
 * really only works well with simple primitive types as the map key. If the key
 * is not a simple primitive then the object is {@code toString}ed and that
 * value is used as its key.
 * 
 * @author Joel Leitch
 */
@SuppressWarnings("unchecked")
final class MapTypeAdapter extends BaseMapTypeAdapter {

	public JsonElement serialize(Map src, Type typeOfSrc,
			JsonSerializationContext context) {
		JsonObject map = new JsonObject();
		Type childGenericType = null;
		if (typeOfSrc instanceof ParameterizedType) {
			Class<?> rawTypeOfSrc = $Gson$Types.getRawType(typeOfSrc);
			childGenericType = $Gson$Types.getMapKeyAndValueTypes(typeOfSrc,
					rawTypeOfSrc)[1];
		}

		for (Map.Entry entry : (Set<Map.Entry>) src.entrySet()) {
			Object value = entry.getValue();

			JsonElement valueElement;
			if (value == null) {
				valueElement = JsonNull.INSTANCE;
			} else {
				Type childType = (childGenericType == null) ? value.getClass()
						: childGenericType;
				valueElement = serialize(context, value, childType);
			}
			map.add(String.valueOf(entry.getKey()), valueElement);
		}
		return map;
	}

	public Map deserialize(JsonElement json, Type typeOfT,
			JsonDeserializationContext context) throws JsonParseException {
		// Use ObjectConstructor to create instance instead of hard-coding a
		// specific type.
		// This handles cases where users are using their own subclass of Map.
		Map<Object, Object> map = constructMapType(typeOfT, context);
		Type[] keyAndValueTypes = $Gson$Types.getMapKeyAndValueTypes(typeOfT,
				$Gson$Types.getRawType(typeOfT));
		for (Map.Entry<String, JsonElement> entry : json.getAsJsonObject()
				.entrySet()) {
			Object key = context.deserialize(new JsonPrimitive(entry.getKey()),
					keyAndValueTypes[0]);
			Object value = context.deserialize(entry.getValue(),
					keyAndValueTypes[1]);
			map.put(key, value);
		}
		return map;
	}

	@Override
	public String toString() {
		return MapTypeAdapter.class.getSimpleName();
	}
}